<!-- OpenCV.js官方文档 https://docs.opencv.org/4.5.5/d0/d84/tutorial_js_usage.html -->
<!-- OpenCV.js官方文档 Image InRange Example https://docs.opencv.org/3.4.8/db/d64/tutorial_js_colorspaces.html -->

<!-- OpenCV.js 官方文档 cvtColor inRange https://docs.opencv.org/4.5.5/db/d64/tutorial_js_colorspaces.html -->
<!-- OpenCV.js 官方文档 Image Threshold https://docs.opencv.org/4.5.5/d7/dd0/tutorial_js_thresholding.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测 https://docs.opencv.org/4.5.5/d0/d43/tutorial_js_table_of_contents_contours.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测-(外接矩形) https://docs.opencv.org/4.5.5/dc/dcf/tutorial_js_contour_features.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测-形状匹配 https://docs.opencv.org/4.5.5/d8/d1c/tutorial_js_contours_more_functions.html -->


<!-- 获取图片中指定颜色部分 https://blog.csdn.net/qq_40456669/article/details/93375709 -->
<!-- opencv数字识别（博文） https://blog.csdn.net/Rosen_er/article/details/104234232 -->
<!-- 信用卡数字识别 https://blog.csdn.net/weixin_43227526/article/details/104653957 -->

<!-- 图像基本运算及掩膜 https://blog.csdn.net/u011028345/article/details/77278467 -->

<!-- 
    遇到的问题
        1. 轮廓绘制时，绘制了多层轮廓。
            cv.drawContours 第8个参数 maxLevel控制绘制层级
        2. opencv.js中如何取 hierarchy 
            https://stackoverflow.com/questions/53102728/opencv-js-iterate-through-contours-hierarchy
            for (let i = 0; i < contours.size(); ++i) {
                let hier = hierarchy.intPtr(0, i);
                /**
                * @link 官方文档 contours_hierarchy https://docs.opencv.org/4.5.5/da/d0a/tutorial_js_contours_hierarchy.html
                * [Next, Previous, First_Child, Parent]
                *   0  ,    1    ,     2      ,    3  ]
                * If there is no child or parent, that field is taken as -1
                */
                if (!(hier[3] === -1 && hier[2] !== -1)) {
                    continue;
                }
            }
        3. 轮廓位置信息获取
            cv.minMaxLoc()只适用于单通道 contours.get(i).channels()=>2
                opencv.js 不支持Mat Mat::reshape(int cn, int rows=0) const
                cv.cvtColor(contours.get(i), dst, cv.COLOR_RGBA2GRAY, 0); 报错
                cv.split(cnt_, planes); console.log(planes.get(0).channels()); 
                    let result = cv.minMaxLoc(planes.get(0), mask_); 获取的坐标数据异常
            正确的方法使用 cv.boundingRect
                let cnt = contours.get(i);
                let rect = cv.boundingRect(cnt);
                let point = new cv.Point(rect.x, rect.y);
    
    todo
        1. 看下 reshape原码，简单的话就js实现
        2. 看一下channels 与 mat的关系 
 -->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>opencv使用——二值化,获取指定范围颜色,轮廓匹配</title>
    <!-- <script src='https://docs.opencv.org/版本号/opencv.js'></script> -->
    <!-- OpenCV.js 4.5.5 版本 -->
    <!-- <script src='https://docs.opencv.org/4.5.5/opencv.js'></script> -->

    <!-- <script src='./opencv_4.5.5.js' type="module"></script> -->

    <!-- You may want to load opencv.js asynchronously by async attribute in <script> tag. To be notified when opencv.js is ready, you can register a callback to onload attribute. -->
    <script async src="./opencv_4.5.5.js" onload="openCvReady()" type="text/javascript"></script>



</head>

<body>
    <!-- ./bag_rune.jpg -->
    <!-- <canvas id="imgSearchCanvas" width="120" height="120"></canvas>
    <canvas id="resultCanvas" width="120" height="120"></canvas> -->

    <!-- ./screenshot1.jpg -->
    <!-- <canvas style="display: none;" id="imgSearchCanvas" width="1080" height="2400"></canvas>
    
    -->
    <canvas style="display: none;" id="imgSearchCanvas" width="120" height="120"></canvas>

    <!-- <canvas id="resultCanvas" width="1080" height="2400"></canvas> -->

    <canvas id="t1" width="120" height="120" class="processing_canvas"></canvas>
    <canvas id="t2" width="120" height="120" class="processing_canvas"></canvas>
    <canvas id="t3" width="120" height="120" class="processing_canvas"></canvas>
    <canvas id="t4" width="120" height="120" class="processing_canvas"></canvas>
    <canvas id="t5" width="120" height="120" class="processing_canvas"></canvas>
    <canvas id="resultCanvas" width="120" height="120" style="display: none;"></canvas>

    <hr />
    <!-- <canvas id="templateCanvasInput" width="70" height="70"></canvas> -->
    <canvas id="templateCanvasInput" width="120" height="120"></canvas>
    <div id="templ_list"></div>

    <!-- ./nums/num_1.jpg -->
    <!-- <canvas id="templateCanvasInput" width="40" height="40"></canvas> -->

    <!-- ./runes/rune_19_COL.jpg -->
    <!-- <canvas id="templateCanvasInput" width="80" height="80"></canvas> -->

    <!-- ./bag_border.png -->
    <!-- <canvas id="templateCanvasInput" width="962" height="795"></canvas> -->

    <!-- <script type="module">
        import * as cv from './opencv_4.5.5.js';
        console.log(cv);
    </script> -->
    <script>
        const NUMS_DATA = [{
            src: "./nums/num_0.png",
            name: "0",
            order: 0
        }, {
            src: "./nums/num_1.png",
            name: "1",
            order: 1
        }, {
            src: "./nums/num_2.png",
            name: "2",
            order: 2
        }, {
            src: "./nums/num_3.png",
            name: "3",
            order: 3
        }, {
            src: "./nums/num_4.png",
            name: "4",
            order: 4
        }, {
            src: "./nums/num_5.png",
            name: "5",
            order: 5
        }, {
            src: "./nums/num_6.png",
            name: "6",
            order: 6
        }, {
            src: "./nums/num_7.png",
            name: "7",
            order: 7
        }, {
            src: "./nums/num_8.png",
            name: "8",
            order: 8
        }, {
            src: "./nums/num_9.png",
            name: "9",
            order: 9
        }];
        let num_imgs = new Map(); // 存放加载的数字图片
        async function openCvReady() {
            // 脚本加载完成
            // console.log(cv);
            // opencv.js 在真正初始化之前加载并触发 onload 事件。为了等到 opencv.js 真正准备好，opencv.js 提供了一个挂机“onRuntimeInitialized”。像这样使用它：

            // var img1 = new Image();
            // img.crossOrigin = 'anonymous';
            // img1.src = './img1.png';
            console.log(1);

            // var src1 = "./img1.png";
            // var src2 = "./img2.png";

            // var src1 = "./icons.png";
            // var src1 = "./bag_rune.jpg";
            // var src2 = "./icon.png";
            // var src2 = "./icon_l.png";

            var src1 = "./screenshot1.jpg";

            // var src2 = "./nums/num_1.jpg";
            // var src2 = "./runes/rune_19_COL.jpg";
            var src2 = "./bag_border.png";

            await Promise.all([
                // 待查询图片加载
                await loadImg(src1).then(img1 => {
                    // console.log(img1);
                    var canvas1 = document.getElementById('imgSearchCanvas');
                    canvas1.width = img1.width;
                    canvas1.height = img1.height;

                    let canvases = document.getElementsByClassName("processing_canvas");
                    // console.log(canvases);
                    [].forEach.bind(canvases)(ele => {
                        ele.width = img1.width;
                        ele.height = img1.height;
                    });

                    var ctx1 = canvas1.getContext('2d');
                    // img1.onload = function () {
                    //     ctx1.drawImage(img1, 0, 0);
                    // }
                    ctx1.drawImage(img1, 0, 0);


                    var canvas3 = document.getElementById('resultCanvas');
                    var ctx3 = canvas3.getContext('2d');
                    ctx3.drawImage(img1, 0, 0);

                    console.log(12);
                })


                // 模板图片加载
                , await loadImg(src2).then(img2 => {
                    // console.log(img2);
                    var canvas2 = document.getElementById('templateCanvasInput');
                    canvas2.width = img2.width;
                    canvas2.height = img2.height;
                    var ctx2 = canvas2.getContext('2d', {
                        willReadFrequently: true,
                    });
                    ctx2.drawImage(img2, 0, 0);
                    console.log(13);
                })


                // 多个模板图片加载
                /*, ...NUMS_DATA.map(async (el) => {
                    let src = el.src;
                    let name = el.name;
                    return (
                        await loadImg(src).then(img => {
                            num_imgs.set(name, img);
                        })
                    );
                })
                */
            ]);

            console.log(2);

            /** @todo 为啥批量加载图片时 cv.onRuntimeInitialized不自动执行了 */
            cv['onRuntimeInitialized'] = () => {
                console.log(3);
                // do all your work here

                /**
                 * 
                 * 二值化图片
                let img = cv.imread("imgSearchCanvas");
                let clone_img = img.clone(); //原图备份

                // cv.imshow('resultCanvas', clone_img);

                cv.cvtColor(img, img, cv.COLOR_BGR2GRAY); //图片转灰度
                // cv.imshow('resultCanvas', img);
                cv.threshold(img, img, 100, 255, cv.THRESH_BINARY); //二值化
                // cv.threshold(img, img, 100, 255, cv.THRESH_BINARY_INV); //二值化
                let clone_img1 = img.clone(); //备份二值化后的原图

                cv.imshow('resultCanvas', clone_img1);
                */

                // 获取图片指定颜色部分 
                let src = cv.imread("imgSearchCanvas");


                // CV_8uc1 单颜色通道 8位,CV_8uc2 2颜色通道 16位, CV_8uc3 3颜色通道 24位,CV_8uc4 4颜色通道 32位


                // console.log("src.type:",src.type()); // 24 cv: cv.CV_8UC4
                let clone_src = src.clone(); //原图备份
                // cv.namedWindow("input", cv.WINDOW_AUTOSIZE);
                // cv.imshow("resultCanvas", src);
                // let hsv = cv.cvtColor(src, src, cv.COLOR_BGR2HSV);

                // num && level
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [240, 0, 0, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [255, 255, 255, 255]);

                // rune
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [141, 174, 183, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [203, 212, 212, 255]);

                // rune 可以被模板匹配使用
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [140, 140, 140, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [200, 200, 200, 255]);

                // 背包范围提取
                let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [40, 40, 40, 255]);
                let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [70, 70, 70, 255]);

                let dst_b_w = new cv.Mat();

                cv.inRange(src, low_hsv, high_hsv, dst_b_w);

                cv.imshow("resultCanvas", dst_b_w);
                dst_b_w = cv.imread("resultCanvas");

                //-------背包区域轮廓匹配
                // let bagSrc = dst_b_w.clone();
                // cv.imshow('t1', bagSrc);

                let dst = new cv.Mat();
                cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY, 0);
                cv.imshow('t1', dst);

                // cv.threshold(dst, dst, 30, 47, cv.THRESH_BINARY);
                cv.threshold(dst, dst, 45, 47, cv.THRESH_BINARY);
                cv.imshow('t2', dst);

                let contours = new cv.MatVector();
                let hierarchy = new cv.Mat();
                let dst1 = cv.Mat.zeros(dst.rows, dst.cols, cv.CV_8UC3);
                // cv.findContours(dst, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);
                // cv.findContours(dst, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);
                cv.findContours(dst, contours, hierarchy, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE);
                console.log(contours, hierarchy);
                // console.log(hierarchy.size());

                let color1 = new cv.Scalar(255, 0, 255); // 轮廓颜色
                let color2 = new cv.Scalar(255, 255, 0); // 外接矩形颜色

                // window.xxx = hierarchy;

                let mask_ = new cv.Mat();
                let order_txt_color = new cv.Scalar(0, 255, 255); // 轮廓序号文字颜色

                /**
                 * 可以根据项目需求在里面搞一下处理，识别出你要找的东西。
                 * 我这里偷懒直接取，下面代码其实可以不要
                 */
                for (let i = 0; i < contours.size(); ++i) {


                    // let ds = hierarchy.get(0, i);// 错误写法 hierarchy是一个cv.Mat
                    // console.log(ds);// 
                    /**
                     * @link https://stackoverflow.com/questions/53102728/opencv-js-iterate-through-contours-hierarchy
                     * If the hierarchy variable was initiated through cv.mat(), the matrix stores the hierarchy values in different data types, like CV_8U,CV_16S etc. Each of which can be accessed using Ptr Manipulation as listed in the table in this link.
                     * https://docs.opencv.org/3.4.3/de/d06/tutorial_js_basic_ops.html
                     * So iteration will look like(here I am accessing the Mat as CV_32S),
                     * 
                     * for (let i = 0; i < contours.size(); ++i) {
                            let hier = hierarchy.intPtr(0, i)
                       }
                     *  Make sure you access the hierarchy in any signed integer format, else you can't check for '-1', for child contour.
                     * 
                     */
                    let hier = hierarchy.intPtr(0, i);
                    // console.log(i,hier);

                    /**
                     * @link 官方文档 contours_hierarchy https://docs.opencv.org/4.5.5/da/d0a/tutorial_js_contours_hierarchy.html
                     * [Next, Previous, First_Child, Parent]
                     *   0  ,    1    ,     2      ,    3  ]
                     * If there is no child or parent, that field is taken as -1
                     */
                    if (!(hier[3] === -1 && hier[2] !== -1)) {
                        continue;
                    }
                    // console.log(hier);
                    let cnt_child = contours.get(hier[2]);
                    let hier_child = hierarchy.intPtr(0, hier[2]);
                    if (hier_child[0] !== -1) {
                        continue;
                    }
                    // console.log("child:",hier[2],hier_child);

                    let cnt = contours.get(i);
                    console.log(i, cnt);
                    /**
                     * cv.minMaxLoc()
                     * The function do not work with multi-channel arrays. If you need to find minimum or maximum elements across all the channels, use Mat::reshape first to reinterpret the array as single-channel. Or you may extract the particular channel using either extractImageCOI , or mixChannels , or split .
                     */

                    // console.log(cnt.channels()); // 2
                    /**
                     * Mat Mat::reshape(int cn, int rows=0) const
                     * cn: 表示通道数(channels), 如果设为0，则表示保持通道数不变，否则则变为设置的通道数。
                     * rows: 表示矩阵行数。 如果设为0，则表示保持原有的行数不变，否则则变为设置的行数。
                     */
                    // let cnt_ = cnt.clone();
                    // cnt_.reshape(1, 0);// opencv.js 不支持 Mat::reshape()

                    // let cnt_ = new cv.Mat();
                    // cv.cvtColor(cnt, cnt_, cv.COLOR_RGBA2GRAY, 0); // 报错
                    // console.log(cnt_.channels()); // 2
                    // let result = cv.minMaxLoc(cnt_, mask_);

                    /*
                    let cnt_ = cnt.clone();
                    let planes = new cv.MatVector();
                    cv.split(cnt_, planes);
                    // console.log(planes.get(0).channels());

                    let result = cv.minMaxLoc(planes.get(0), mask_);
                    // let minVal = result.minVal;
                    // let maxVal = result.maxVal;
                    // let minLoc = result.minLoc;
                    let maxLoc = result.maxLoc;
                    if (i < 10) {
                        // 1-9 1个文字
                        let point = new cv.Point(16 + maxLoc.x, maxLoc.y + 32);
                        cv.putText(dst1, i + "", point, cv.FONT_HERSHEY_SIMPLEX, 1, order_txt_color, 3, cv
                            .LINE_8); // 标序号

                    } else {
                        // 10-？ 两个文字
                        let point = new cv.Point(8 + maxLoc.x, maxLoc.y + 32);
                        cv.putText(dst1, i + "", point, cv.FONT_HERSHEY_SIMPLEX, 1, order_txt_color, 3, cv
                            .LINE_8); // 标序号
                    }
                    */
                    let order_txt_color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math
                        .random() * 255), Math.round(Math.random() * 255));
                    // cv.drawContours (image, contours, contourIdx, color, thickness = 1, lineType = cv.LINE_8, hierarchy = new cv.Mat(), maxLevel = INT_MAX, offset = new cv.Point(0, 0))
                    // cv.drawContours(dst1, contours, i, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                    cv.drawContours(dst1, contours, i, color1, 1, cv.LINE_8, hierarchy, 1); // 轮廓绘制


                    // 外接矩形绘制

                    let rect = cv.boundingRect(cnt);
                    let point1 = new cv.Point(rect.x, rect.y);
                    let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);

                    if (i < 10) {
                        // 1-9 1个文字
                        let point = new cv.Point(16 + rect.x, rect.y + 32);
                        cv.putText(dst1, i + "", point, cv.FONT_HERSHEY_SIMPLEX, 0.4, order_txt_color, 1, cv
                            .LINE_4); // 标序号

                    } else {
                        // 10-？ 两个文字
                        let point = new cv.Point(8 + rect.x, rect.y + 32);
                        cv.putText(dst1, i + "", point, cv.FONT_HERSHEY_SIMPLEX, 0.4, order_txt_color, 1, cv
                            .LINE_4); // 标序号
                    }

                    cv.rectangle(dst1, point1, point2, color2, 1, cv.LINE_AA, 0); // 绘制外接矩形
                    cnt.delete();
                }


                cv.imshow('t3', dst1);

                // 背包区域
                let cont_bag_bd = contours.get(6); // 6是背包轮廓序号，我这里直接写死（不考虑截图异常情况）
                let bag_rect = cv.boundingRect(cont_bag_bd);
                let bag_scr = clone_src.roi(bag_rect).clone();

                cv.imshow('t4', bag_scr);


                // rune
                low_hsv = new cv.Mat(bag_scr.rows, bag_scr.cols, bag_scr.type(), [141, 174, 183, 255]);
                high_hsv = new cv.Mat(bag_scr.rows, bag_scr.cols, bag_scr.type(), [203, 212, 212, 255]);

                let bag_b_w = new cv.Mat();
                cv.inRange(bag_scr, low_hsv, high_hsv, bag_b_w);
                cv.imshow("t4", bag_b_w);
                bag_b_w = cv.imread("t4");


                contours = new cv.MatVector();
                hierarchy = new cv.Mat();
                dst = new cv.Mat();
                cv.cvtColor(bag_b_w, dst, cv.COLOR_RGBA2GRAY, 0);
                cv.imshow("t5", dst);

                dst1 = cv.Mat.zeros(dst.rows, dst.cols, cv.CV_8UC3);

                // cv.threshold(dst, dst, 45, 47, cv.THRESH_BINARY);

                // cv.findContours(dst, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);
                // cv.findContours(dst, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);
                cv.findContours(dst, contours, hierarchy, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE);
                console.log("contours.size:", contours.size());
                for (let i = 0; i < contours.size(); ++i) {
                    let hier = hierarchy.intPtr(0, i);
                    // console.log(i,hier);
                    // [Next, Previous, First_Child, Parent]
                    if (!(hier[3] === -1 && hier[2] !== -1)) {
                        continue;
                    }
                    // console.log(hier);
                    // let cnt_child = contours.get(hier[2]);
                    // let hier_child = hierarchy.intPtr(0, hier[2]);
                    // if (hier_child[0] !== -1) {
                    //     continue;
                    // }

                    let cnt = contours.get(i);
                    // console.log(i, cnt);
                    let order_txt_color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math
                        .random() * 255), Math.round(Math.random() * 255));
                    // cv.drawContours (image, contours, contourIdx, color, thickness = 1, lineType = cv.LINE_8, hierarchy = new cv.Mat(), maxLevel = INT_MAX, offset = new cv.Point(0, 0))
                    // cv.drawContours(dst1, contours, i, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                    cv.drawContours(dst1, contours, i, color1, 1, cv.LINE_8, hierarchy, 1); // 轮廓绘制

                    // 外接矩形绘制
                    let rect = cv.boundingRect(cnt);
                    let point1 = new cv.Point(rect.x, rect.y);
                    let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);

                    let point = new cv.Point(10 + rect.x, rect.y + 10);
                    cv.putText(dst1, i + "", point, cv.FONT_HERSHEY_SIMPLEX, 0.4, order_txt_color, 1, cv
                        .LINE_4); // 标序号

                    cv.rectangle(dst1, point1, point2, color2, 1, cv.LINE_AA, 0); // 绘制外接矩形
                    cnt.delete();
                }

                cv.imshow("t5", dst1);


                mask_.delete();

                /*
                //-------数字轮廓匹配
                let numRect = new cv.Rect(60, 60, 60, 60);// 识别数字只选择图片右下角
                let num_b_w = dst_b_w.roi(numRect).clone();
                // let num_b_w = dst_b_w.clone();
                // num_b_w.convertTo(num_b_w, cv.CV_8UC4); // 没用


                

                cv.imshow('t1', num_b_w);
                src = num_b_w;
                // console.log("src.type:",src.type()); // 24 cv: cv.CV_8UC4
                let contours = new cv.MatVector();
                let hierarchy = new cv.Mat();
                let dst = cv.Mat.zeros(src.cols, src.rows, cv.CV_8UC3);
                // console.log("dst.type:",dst.type()); // 16 cv: cv.CV_8UC3


                // 将三通道图像（彩色图）转化为单通道图（灰度图）；第三个参数flag 转换模式 cv2.COLOR_RGB2GRAY 彩色转灰度 cv2.COLOR_GRAY2RGB 单通道转多通道
                cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U
                cv.imshow('t2', src);


                cv.threshold(src, src, 220, 255, cv.THRESH_BINARY);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U
                cv.imshow('t3', src);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U

                cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

                let color1 = new cv.Scalar(255, 0, 255); // 轮廓颜色
                let color2 = new cv.Scalar(255, 255, 0); // 外接矩形颜色
                
                // for (let i = 0; i < contours.size(); ++i) {
                //     // let color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255));

                //     cv.drawContours(dst, contours, i, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制

                //     // 外接矩形绘制
                //     let cnt = contours.get(i);
                //     let rect = cv.boundingRect(cnt);
                //     let point1 = new cv.Point(rect.x, rect.y);
                //     let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);
                //     cv.rectangle(dst, point1, point2, color2, 1, cv.LINE_AA, 0);// 绘制外接矩形
                //     cnt.delete();
                // }
                // cv.imshow('t4', dst);
                
                let contours_order = 1;
                cv.drawContours(dst, contours, contours_order, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                
                let cnt = contours.get(contours_order);
                let rect = cv.boundingRect(cnt);
                let point1 = new cv.Point(rect.x, rect.y);
                let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);
                let dst_c = dst.clone();
                cv.rectangle(dst, point1, point2, color2, 1, cv.LINE_AA, 0);// 外接矩形绘制
                cnt.delete();
                cv.imshow('t4', dst);
                dst = dst_c.roi(rect);
                cv.imshow('t5', dst);
                contours.delete();
                hierarchy.delete();
                dst.delete();
                

                src.delete();
                */

            };




        }

        function _Rect(pt1, pt2) {
            let x = pt1.x < pt2.x ? pt1.x : pt2.x;
            let y = pt1.y < pt2.y ? pt1.y : pt2.y;
            let w = pt1.x > pt2.x ? pt1.x : pt2.x - x;
            let h = pt1.y > pt2.y ? pt1.y : pt2.y - y;
            return new cv.Rect(x, y, w, h);
        }

        function loadImg(url) {
            return new Promise((resolve, reject) => {
                let img = new Image();
                // img.crossOrigin = 'anonymous';
                // 跨域 直接用live server运行
                img.src = url;
                img.onload = () => {
                    resolve(img);
                };
                img.onerror = reject;
            });
        }

        /***
         * @function
         * @description 两个图像相似度比较。
         * @description 最后就是模板匹配了，匹配的方式有很多种，这里采用的是对数字和模板进行像素点的匹配，根据相同像素点的百分占比来得出结论，相同像素点越多，说明匹配相似度越高，最后以匹配程度最高的作为最终识别结果
         * @param { cv.Mat } img1 注意保证两个图的通道数一致
         * @param { cv.Mat } img2 注意保证两个图的通道数一致
         * 
         * @returns { float } [0,1]
         * @link https://blog.csdn.net/m0_71741835/article/details/127937354
         * 
         */
        function mmCompare(img1, img2) {
            if (img1.channels() !== img2.channels()) {
                console.error("img1与img2通道数不一致");
                return 0;
            }
            // console.log(img1.channels(), img2.channels());
            let my_temp = new cv.Mat();
            if (img1.cols > img2.cols) {
                // console.log("xxx");
                cv.resize(img2, my_temp, img1.size());
            } else {
                // console.log("yyy");
                cv.resize(img1, my_temp, img2.size());
            }
            let rows, cols;
            let img_point, temp_point; //像素类型uchar

            // console.log(my_temp.rows, img1.rows, img2.rows);
            // console.log(my_temp.cols, img1.cols, img2.cols);
            rows = my_temp.rows;
            cols = my_temp.cols * img2.channels();

            let result, same = 0.0,
                different = 0.0;
            for (let i = 0; i < rows; i++) //遍历图像像素
            {
                //获取像素值
                if (img1.cols > img2.cols) {
                    img_point = img1.ucharPtr(i);
                } else {
                    img_point = img2.ucharPtr(i);
                }
                temp_point = my_temp.ucharPtr(i);
                for (let j = 0; j < cols; j++) {
                    // let test1 = img_point[j];
                    // let test2 = temp_point[j];
                    // console.log(test1, test2);
                    if (img_point[j] == temp_point[j]) {
                        same++; //记录像素相同的个数
                        // if (img_point[j] !== 0) {
                        //     same++;//记录像素相同的个数
                        // }
                    } else {
                        different++; //记录像素不同的个数
                    }

                }
            }
            result = same / (same + different);
            my_temp.delete();
            return result; //返回匹配结果
        }
    </script>
</body>

</html>